const DEBOUNCE_TIMER_MAP = {} as { [x: string]: any }
/**
 * 防抖，规定时间内，只触发一次。触发后需要在等待时间结束后才能第二次执行
 * @param {Function} 执行函数
 * @param {object} params
 * @param {number} params.wait 延时ms
 * @param {string} params.name 多个防抖函数执行需加唯一名字保证互不影响
 */
export function debounce(func: () => void, params = {} as { wait: number, name?: string, immediately?: boolean }) {
  const timeName = `_debounceTime${params.name || ''}`

  const wait = params.wait || 600
  const immediately = params.immediately
  !DEBOUNCE_TIMER_MAP[timeName] && immediately && func && func()
  DEBOUNCE_TIMER_MAP[timeName] && clearTimeout(DEBOUNCE_TIMER_MAP[timeName])
  DEBOUNCE_TIMER_MAP[timeName] = setTimeout(() => {
    func && func()
  }, wait)
}

export function debounceFun<F extends (...args: any[]) => void>(func: F, waitFor: number) {
  let timeout: ReturnType<typeof setTimeout> | null = null

  const debounced = (...args: Parameters<F>) => {
    if (timeout !== null) {
      clearTimeout(timeout)
      timeout = null
    }
    timeout = setTimeout(() => func(...args), waitFor)
  }

  return debounced as (...args: Parameters<F>) => ReturnType<F>
}

/**
 * 函数节流 // 节流，在规定时间内连续触发时只有最后一次触发生效
 * @param {Function} fn
 * @param {object} params
 * @param {number} params.wait 间隔时间
 * @param {string} params.immediately 第一次是否立即执行
 * @param {string} params.name 多个节流函数执行需加唯一名字保证互不影响
 */
const THROTTLE_TIMER_MAP = {} as { [x: string]: any }
export function throttle(func: () => void, params = {} as { wait: number, name?: string, immediately?: boolean }) {
  const timeName = `_throttleTime${params.name || ''}`
  if (THROTTLE_TIMER_MAP[timeName]) return
  const wait = params.wait || 600
  const immediately = params.immediately

  !THROTTLE_TIMER_MAP[timeName] && immediately && func && func()

  THROTTLE_TIMER_MAP[timeName] = setTimeout(() => {
    !immediately && func && func()
    clearTimeout(THROTTLE_TIMER_MAP[timeName])
    delete THROTTLE_TIMER_MAP[timeName]
  }, wait)
}

// 二维数组去重
export function getUnique(array: any[]) {
  const map = new Map()
  return array.filter((item: string, index: number) => {
    const newItem = JSON.stringify(item)
    if (map.has(newItem)) {
      return false
    }
    map.set(newItem, true)
    return true
  })
}

export function trim(str: string | null) {
  if (String.prototype.trim) {
    return str === null ? '' : String.prototype.trim.call(str)
  }
  else {
    return str?.replace(/(^\s*)|(\s*$)/g, '') || str
  }
}

export function trimAll(str: string) {
  return str.replace(/\s*/g, '')
}

// 解析 path
export function parseUrl(fullPath = '') {
  const [path, queryStr] = fullPath.split('?')
  const name = path.slice(path.lastIndexOf('/') + 1)
  const query: Record<string, string> = {}
  queryStr && queryStr.split('&').map((i) => i.split('=')).forEach((i) => (query[i[0]] = i[1]))
  return {
    name,
    path,
    query
  }
}

export function obj2url(obj: { [x: string]: string | number | boolean }) {
  return Object.keys(obj).map((k) => {
    return `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`
  }).join('&')
}

export function getStrLength(str: string) {
  return str.replace(/[\u0391-\uFFE5]/g, 'aa').length
}

export function stripBOM(content: string) {
  // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
  // because the buffer-to-string conversion in `fs.readFileSync()`
  // translates it to FEFF, the UTF-16 BOM.
  if (content.charCodeAt(0) === 0xFEFF) {
    content = content.slice(1)
  }
  return content
}

// 深度克隆
/**
 * 深度克隆一个对象或数组
 * @param obj 要克隆的对象或数组
 * @returns 克隆后的对象或数组
 */
export function cloneDeep<T>(obj: any): T {
  // 如果传入的对象为空，返回空
  if (obj === null) {
    return null as unknown as T
  }

  // 如果传入的对象是 Set 类型，则将其转换为数组，并通过新的 Set 构造函数创建一个新的 Set 对象
  if (obj instanceof Set) {
    return new Set([...obj]) as unknown as T
  }

  // 如果传入的对象是 Map 类型，则将其转换为数组，并通过新的 Map 构造函数创建一个新的 Map 对象
  if (obj instanceof Map) {
    return new Map([...obj]) as unknown as T
  }

  // 如果传入的对象是 WeakMap 类型，则直接用传入的 WeakMap 对象进行赋值
  if (obj instanceof WeakMap) {
    let weakMap = new WeakMap()
    weakMap = obj
    return weakMap as unknown as T
  }

  // 如果传入的对象是 WeakSet 类型，则直接用传入的 WeakSet 对象进行赋值
  if (obj instanceof WeakSet) {
    let weakSet = new WeakSet()
    weakSet = obj
    return weakSet as unknown as T
  }

  // 如果传入的对象是 RegExp 类型，则通过新的 RegExp 构造函数创建一个新的 RegExp 对象
  if (obj instanceof RegExp) {
    return new RegExp(obj) as unknown as T
  }

  // 如果传入的对象是 undefined 类型，则返回 undefined
  if (typeof obj === 'undefined') {
    return undefined as unknown as T
  }

  // 如果传入的对象是数组，则递归调用 cloneDeep 函数对数组中的每个元素进行克隆
  if (Array.isArray(obj)) {
    return obj.map(cloneDeep) as unknown as T
  }

  // 如果传入的对象是 Date 类型，则通过新的 Date 构造函数创建一个新的 Date 对象
  if (obj instanceof Date) {
    return new Date(obj.getTime()) as unknown as T
  }

  // 如果传入的对象是普通对象，则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆
  if (typeof obj === 'object') {
    const newObj: any = {}
    for (const [key, value] of Object.entries(obj)) {
      newObj[key] = cloneDeep(value)
    }
    const symbolKeys = Object.getOwnPropertySymbols(obj)
    for (const key of symbolKeys) {
      newObj[key] = cloneDeep(obj[key])
    }
    return newObj
  }

  // 如果传入的对象是基本数据类型（如字符串、数字等），则直接返回
  return obj
}

// JS对象深度合并
export function deepMerge(target: Record<string, any> = {}, source: Record<string, any> = {}) {
  target = cloneDeep(target)
  if (typeof target !== 'object' || typeof source !== 'object') return false
  for (const prop in source) {
    if (!Object.prototype.hasOwnProperty.call(source, prop)) continue
    if (prop in target) {
      if (typeof target[prop] !== 'object') {
        target[prop] = source[prop]
      }
      else {
        if (typeof source[prop] !== 'object') {
          target[prop] = source[prop]
        }
        else {
          if (target[prop].concat && source[prop].concat) {
            target[prop] = target[prop].concat(source[prop])
          }
          else {
            target[prop] = deepMerge(target[prop], source[prop])
          }
        }
      }
    }
    else {
      target[prop] = source[prop]
    }
  }
  return target
}

/**
 * @description 选出对象中需要的字段
 * @param {object} obj 操作的对象
 * @param {string[]} keys ['key1', 'key2']
 */
export function pick(obj: { [x: string]: any }, keys: string[]) {
  if (!Array.isArray(keys)) throw new Error('去除字段必须为数组')
  const temp: Record<string, any> = {}
  for (const key in obj) {
    if (keys.includes(key)) temp[key] = obj[key]
  }
  return temp
}

/**
 * @description 排除对象中不需要的字段
 * @param {object} obj 操作的对象
 * @param {string[]} keys ['key1', 'key2']
 */
export function omit(obj: { [x: string]: any }, keys: string[]) {
  if (!Array.isArray(keys)) throw new Error('去除字段必须为数组')
  const temp: Record<string, any> = {}
  for (const key in obj) {
    if (keys.includes(key)) continue
    temp[key] = obj[key]
  }
  return temp
}

// Determine if it is a JSON string
export function isJSON(str: string) {
  if (typeof str === 'string') {
    try {
      const data = JSON.parse(str)
      return !!data
    }
    catch (_) {
      return false
    }
  }
  return false
}

/**
 * @description JSON字符串转对象
 * @param {string} str
 * @returns {object}
 */
export function jsonToObject(str: string) {
  if (typeof str === 'string') {
    try {
      return JSON.parse(str)
    }
    catch (_) {
      return null
    }
  }
  return str
}

export function groupBy(list: any[], key: string | number) {
  return list.reduce((obj: { [x: string]: any[] }, item: { [x: string]: string | number }) => {
    if (!item[key]) return obj
    obj[item[key]] ? obj[item[key]].push(item) : obj[item[key]] = [item]
    return obj
  }, {})
}

/**
 * 缓存函数，缓存复杂函数计算可使用
 * @param {*} fn
 * @returns
 */
export function memoize(fn: (arg0: any) => any) {
  return (
    (cache = Object.create(null)) =>
      (arg: string | number) =>
        cache[arg] || (cache[arg] = fn(arg))
  )()
}
